#include "app_ota.h"

#define OTA_TIMEOUT (5000)

typedef struct {
    unsigned int size;
    unsigned int offset;
    unsigned int crc32;
    unsigned char running;
    unsigned int time;
} app_ota_t;

volatile static app_ota_t sg_ota_mgr = {0x00};
static void app_ota_boot_set(void)
{
    unsigned char buff[USER_FLASH_APP_FLAG_SIZE] = {
        0x45, 0x69, 0x48, 0x70, 0x6c, 0x4b, 0x4c, 0x7a, 0x42, 0x47, 
        0x2b, 0x36, 0x60, 0x47, 0x4f, 0x4f, 0x4c, 0x68, 0x4b, 0x6d, 
        0x78, 0x67, 0x2c, 0x28, 0x7a, 0x79, 0x49, 0x57, 0x3b, 0x68, 
        0x3b, 0x61, 0x7c, 0x73, 0x42, 0x5a, 0x45, 0x6b, 0x52, 0x21, 
        0x33, 0x37, 0x76, 0x65, 0x72, 0x7a, 0x61, 0x77, 0x24, 0x61, 
        0x33, 0x25, 0x38, 0x53, 0x73, 0x71, 0x42, 0x68, 0x61, 0x68, 
        0x75, 0x60, 0x78, 0x23
    };
    
    drv_flash_write(USER_FLASH_APP_FLAG_ADDR, buff, USER_FLASH_APP_FLAG_SIZE);
    NVIC_SystemReset();
}

static void app_ota_file_crc32_feed(unsigned int buff[], unsigned int length)
{
    for (unsigned int i = 0; i < length; i++) {
        LL_CRC_FeedData32(CRC, buff[i]);
    }
}

static unsigned char app_ota_file_crc32_verify(void)
{
    LL_CRC_ResetCRCCalculationUnit(CRC);

    unsigned int offset = 0x00;
    unsigned char buff[256] = {0x00};

    do {
        memset(buff, 0x00, 256);
        drv_flash_read(USER_FLASH_APP2_BASE+offset, buff, 256);
        app_ota_file_crc32_feed((unsigned int *)buff, 0x40);
        
        offset += 256;
    } while (offset < sg_ota_mgr.size);

    unsigned int crc32 = LL_CRC_ReadData32(CRC);
    if (crc32 != sg_ota_mgr.crc32) {
        return 0x00;
    }

    return 0x01;
}

void app_ota_boot_check(void)
{
    unsigned char buff[USER_FLASH_APP_FLAG_SIZE] = {0x00};
    drv_flash_read(USER_FLASH_APP_FLAG_ADDR, buff, USER_FLASH_APP_FLAG_SIZE);

    if (buff[0] != 0xFF) {
        drv_flash_erase(USER_FLASH_APP_FLAG_ADDR, USER_FLASH_APP_FLAG_SIZE);
    }
}

void app_ota_notif_cb_handler(const unsigned char *data, unsigned short length)
{
    ret_if_true(NULL == data || 0x04 != length)

    memset((void *)&sg_ota_mgr, 0x00, sizeof(app_ota_t));
    sg_ota_mgr.size = (data[0] << 24) | (data[1] << 16) | (data[2] << 8) | (data[3]);
    ret_if_true(sg_ota_mgr.size <= 0x04);

    sg_ota_mgr.size -= 0x04;
    drv_flash_erase(USER_FLASH_APP2_BASE, USER_FLASH_APP_SIZE);

    app_uart_tx_ota_notif();
    sg_ota_mgr.running = 0x01;
    sg_ota_mgr.time = app_sys_ticks;
}

void app_ota_trans_cb_handler(const unsigned char *data, unsigned short length)
{
    ret_if_true(NULL == data || 0x04 > length || length > 0x104)
    ret_if_true(sg_ota_mgr.size == 0)

    // bin打包为升级文件会进行256字节对齐，并且追加4字节CRC32校验值
    unsigned int offset = (data[0] << 24) | (data[1] << 16) | (data[2] << 8) | (data[3]);

    if (offset < sg_ota_mgr.size) {
        unsigned char buff[256] = {0x00};
        memcpy(buff, &data[4], length - 4);
        drv_flash_write(USER_FLASH_APP2_BASE + offset, (unsigned char *)buff, 0x100);
    } else if (length == 0x08 && offset >= sg_ota_mgr.size) {
        sg_ota_mgr.crc32 = (data[7] << 24) | (data[6] << 16) | (data[5] << 8) | (data[4]);
    } else if (length == 0x04 && offset >= sg_ota_mgr.size) {
        if (app_ota_file_crc32_verify()) {
            app_ota_boot_set();
        }
    }

    app_uart_tx_ota_trans();
    sg_ota_mgr.time = app_sys_ticks;
}

unsigned char app_ota_is_running(void)
{
    if (sg_ota_mgr.running == 0x01 && !time_after(app_sys_ticks, sg_ota_mgr.time + OTA_TIMEOUT)) {
        return true;
    }
    sg_ota_mgr.running = 0x00;
    return false;
}